#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File  : 原生调用.py
# @Author: anarchy
# @Date  : 2025/5/16
# @Desc  : 智能PPT生成 - 最小原生调用示例

import hashlib
import hmac
import base64
import json
import time
import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder
from typing import Optional, Dict, Any # 类型提示

class HttpAuthParams:
    """处理智能PPT HTTP API认证参数和头部生成的辅助类 (Minimal version)"""
    def __init__(self, app_id: str, api_secret: str):
        """
        初始化认证参数处理器。
        :param app_id: 智能PPT服务的APPID。
        :param api_secret: 智能PPT服务的APISecret。
        """
        if not app_id or not api_secret:
            # Using print for minimal example, consider using a proper logger
            print("Error: APPID 和 APISecret 不能为空。")
            raise ValueError("APPID 和 APISecret 不能为空。")
        self.app_id = app_id
        self.api_secret = api_secret

    def _md5(self, text: str) -> str:
        """计算文本的MD5哈希值。"""
        return hashlib.md5(text.encode('utf-8')).hexdigest()

    def _hmac_sha1_encrypt(self, encrypt_text: str, encrypt_key: str) -> str:
        """使用HMAC-SHA1算法加密文本，并返回Base64编码的摘要。"""
        return base64.b64encode(
            hmac.new(encrypt_key.encode('utf-8'), encrypt_text.encode('utf-8'), hashlib.sha1).digest()
        ).decode('utf-8')

    def _get_signature(self, ts: int) -> str:
        """生成API请求所需的签名。"""
        auth = self._md5(self.app_id + str(ts))
        return self._hmac_sha1_encrypt(auth, self.api_secret)

    def generate_headers(self, content_type: str = "application/json; charset=utf-8") -> Dict[str, str]:
        """生成通用的API请求头部。"""
        timestamp = int(time.time())
        signature = self._get_signature(timestamp)
        return {
            "appId": self.app_id,
            "timestamp": str(timestamp),
            "signature": signature,
            "Content-Type": content_type
        }


class PPTGenerator():
    """封装智能PPT生成相关API的类 (Minimal version)"""
    BASE_URL = "https://zwapi.xfyun.cn/api/ppt/v2"  # API基础地址
    CREATE_TASK_URL = f"{BASE_URL}/create"  # 创建PPT任务（直接基于文本或文档）
    PROGRESS_URL = f"{BASE_URL}/progress"  # 查询任务进度
    TEMPLATE_LIST_URL = f"{BASE_URL}/template/list"  # 获取PPT模板列表
    CREATE_OUTLINE_URL = f"{BASE_URL}/createOutline"  # 根据文本创建大纲
    CREATE_OUTLINE_BY_DOC_URL = f"{BASE_URL}/createOutlineByDoc"  # 根据文档创建大纲
    CREATE_PPT_BY_OUTLINE_URL = f"{BASE_URL}/createPptByOutline"  # 根据大纲创建PPT


    def __init__(self, appid: str, apikey: str, apisecret: str):
        """
        初始化PPT生成器实例。

        :param appid: 智能PPT服务的APPID。
        :param apikey: 智能PPT服务的APIKey (注意: 此版本中主要使用appid和apisecret进行认证)。
        :param apisecret: 智能PPT服务的APISecret。
        :raises ValueError: 如果缺少必要的APPID或APISecret。
        """
        if not appid or not apisecret:
            print("错误: APPID 和 APISecret 不能为空。") # 使用中文提示
            raise ValueError("APPID 和 APISecret 不能为空。")

        self.appid = appid
        self.apikey = apikey 
        self.apisecret = apisecret

        self.auth_handler = HttpAuthParams(app_id=self.appid, api_secret=self.apisecret)
        print("PPTGenerator 已使用提供的凭证成功初始化。")

    def get_theme_list(self, pay_type: str = "not_free", page_num: int = 1, page_size: int = 10) -> Optional[Dict[str, Any]]:
        """
        获取PPT主题模板列表 (Minimal example).

        :param pay_type: 付费类型 (例如: "free", "not_free", "vip"), 默认 "not_free"。
        :param page_num: 页码，从1开始，默认1。
        :param page_size: 每页数量，默认10。
        :return: 包含主题列表的字典，或在失败时返回None。
        """
        print(f"尝试获取主题列表，付费类型: {pay_type}, 页码: {page_num}, 每页数量: {page_size}")
        params = {
            "payType": pay_type,
            "pageNum": page_num,
            "pageSize": page_size
        }
        
        headers = self.auth_handler.generate_headers()
        print(f"生成的头部信息: {headers}")

        try:
            response = requests.get(self.TEMPLATE_LIST_URL, headers=headers, params=params)
            print(f"GET 请求到 {self.TEMPLATE_LIST_URL}，参数: {params}")
            print(f"响应状态码: {response.status_code}")
            # print(f"响应体: {response.text}") # 可选：打印完整响应体进行调试
            
            if response.status_code == 200:
                resp_json = response.json()
                if resp_json.get('code') == 0:
                    print("成功获取主题列表。")
                    return resp_json.get('data')
                else:
                    print(f"API 错误: code={resp_json.get('code')}, message='{resp_json.get('message')}', sid='{resp_json.get('sid')}'")
                    print(f"完整响应: {response.text}")
                    return None
            else:
                print(f"HTTP 错误: {response.status_code}, 响应: {response.text}")
                return None
        except requests.RequestException as e:
             print(f"请求失败: {e}")
             return None
        except json.JSONDecodeError:
            print(f"解析JSON响应失败: {response.text}")
            return None

    def create_ppt_from_text(self, query_text: str, template_id: str, author: str = "AI助手",
                               is_card_note: bool = True, search: bool = False, is_figure: bool = True,
                               ai_image_type: str = "normal") -> Optional[str]:
        """
        直接根据输入文本创建PPT (Minimal example).
        :return: 任务的SID (字符串)，或在失败时返回None。
        """
        print(f"尝试根据文本创建PPT，主题: '{query_text[:50]}...'")
        fields = {
            "query": query_text,
            "templateId": template_id,
            "author": author,
            "isCardNote": str(is_card_note).lower(),
            "search": str(search).lower(),
            "isFigure": str(is_figure).lower(),
            "aiImage": ai_image_type
        }
        encoder = MultipartEncoder(fields=fields)
        headers = self.auth_handler.generate_headers(content_type=encoder.content_type)
        print(f"创建PPT头部信息 (Content-Type: {encoder.content_type})")

        try:
            response = requests.post(self.CREATE_TASK_URL, data=encoder, headers=headers)
            print(f"POST 请求到 {self.CREATE_TASK_URL}")
            print(f"响应状态码: {response.status_code}")
            # print(f"响应体: {response.text}")

            if response.status_code == 200:
                resp_json = response.json()
                if resp_json.get('code') == 0:
                    sid = resp_json.get('data', {}).get('sid')
                    if sid:
                        print(f"成功启动PPT创建任务，SID: {sid}")
                        return sid
                    else:
                        print(f"API成功但未返回SID。响应: {resp_json}")
                        return None
                else:
                    print(f"API 错误: code={resp_json.get('code')}, message='{resp_json.get('message')}', sid='{resp_json.get('sid')}'")
                    print(f"完整响应: {response.text}")
                    return None
            else:
                print(f"HTTP 错误: {response.status_code}, 响应: {response.text}")
                return None
        except requests.RequestException as e:
            print(f"请求失败: {e}")
            return None
        except json.JSONDecodeError:
            print(f"解析JSON响应失败: {response.text}")
            return None

    def get_task_progress(self, sid: str) -> Optional[Dict[str, Any]]:
        """
        查询指定SID的任务的当前进度 (Minimal example).
        :return: 包含任务状态信息的字典，或在失败时返回None。
        """
        print(f"尝试查询任务进度，SID: {sid}")
        if not sid:
            print("错误: 必须提供SID才能查询进度。")
            return None
        
        headers = self.auth_handler.generate_headers()
        # print(f"查询进度头部信息: {headers}") # 通常与get_theme_list的类似，可省略重复打印

        try:
            response = requests.get(f"{self.PROGRESS_URL}?sid={sid}", headers=headers)
            print(f"GET 请求到 {self.PROGRESS_URL}?sid={sid}")
            print(f"响应状态码: {response.status_code}")
            # print(f"响应体: {response.text}")
            
            if response.status_code == 200:
                resp_json = response.json()
                if resp_json.get('code') == 0:
                    print(f"成功查询任务进度。数据: {resp_json.get('data')}")
                    return resp_json.get('data')
                else:
                    print(f"API 错误 (查询进度): code={resp_json.get('code')}, message='{resp_json.get('message')}', sid='{resp_json.get('sid')}'")
                    print(f"完整响应: {response.text}")
                    return None
            else:
                print(f"HTTP 错误 (查询进度): {response.status_code}, 响应: {response.text}")
                return None
        except requests.RequestException as e:
            print(f"请求失败 (查询进度): {e}")
            return None
        except json.JSONDecodeError:
            print(f"解析JSON响应失败 (查询进度): {response.text}")
            return None

    def poll_for_result(self, sid: str, poll_interval: int = 10, max_attempts: int = 30) -> Optional[str]:
        """
        轮询任务直到完成或超时，返回最终的PPT链接 (Minimal example).
        :return: 成功生成PPT的URL，或在失败/超时时返回None。
        """
        print(f"开始轮询PPT任务结果，SID: {sid} (间隔: {poll_interval}s, 最大尝试: {max_attempts}次)")
        if not sid:
            print("错误: 必须提供SID才能轮询结果。")
            return None

        for attempt in range(max_attempts):
            print(f"轮询尝试 {attempt + 1}/{max_attempts}...")
            progress_data = self.get_task_progress(sid) # 此处会打印 get_task_progress 的日志
            
            if progress_data:
                ppt_status = progress_data.get('pptStatus')
                # 可以根据需要添加对 aiImageStatus 和 cardNoteStatus 的检查
                print(f"  当前轮询状态: PPT状态={ppt_status}")

                # 检查所有相关状态是否都已完成 ("done")
                all_done = ppt_status == 'done'
                # 可选: 检查其他状态，如果它们在创建时被启用
                # if 'aiImageStatus' in progress_data and progress_data.get('aiImageStatus') is not None:
                #     all_done = all_done and (progress_data.get('aiImageStatus') == 'done')
                # if 'cardNoteStatus' in progress_data and progress_data.get('cardNoteStatus') is not None:
                #     all_done = all_done and (progress_data.get('cardNoteStatus') == 'done')

                if all_done:
                    ppt_url = progress_data.get('pptUrl')
                    if ppt_url:
                        print(f"PPT生成成功！URL: {ppt_url}")
                        return ppt_url
                    else:
                        print(f"所有相关状态均为 'done' 但缺少 pptUrl。数据: {progress_data}")
                        return None # 状态完成但无URL，异常情况
                
                failed_statuses = ['failed', 'error', 'fail'] # 可能的失败状态字符串
                if ppt_status and ppt_status in failed_statuses: # 简化检查
                    print(f"任务失败。SID: {sid}, PPT状态: {ppt_status}")
                    return None
            else: 
                print(f"获取任务 {sid} 的进度失败 (轮询尝试 {attempt + 1})。将重试...")

            time.sleep(poll_interval)
        
        print(f"轮询超时 ({max_attempts * poll_interval} 秒) 后，任务 {sid} 仍未完成。")
        return None

# Example Usage (Minimal)
if __name__ == '__main__':
    # 重要: 请替换为您的讯飞星火大模型API凭证
    # 您可以从环境变量中加载，或者直接在此处填写
    test_appid = "your_appid"  # 请替换为您的讯飞 APPID
    test_apikey = "your_apikey" # 请替换为您的讯飞 APIKEY
    test_apisecret = "your_apisecret" # 请替换为您的讯飞 APISECRET

    # 初始化PPT生成器实例
    ppt_gen_instance = PPTGenerator(appid=test_appid, apikey=test_apikey, apisecret=test_apisecret)

    # --- 直接根据文本创建PPT ---
    print("\n--- 演示: 直接根据文本创建PPT ---") 
    
    # 重要: 请确保这是一个有效的模板ID
    selected_template_id = "20240718489569D" 

    direct_ppt_query = "介绍一下大语言模型的最新进展"
    print(f"PPT主题: \"{direct_ppt_query}\"") 
    
    sid_direct = ppt_gen_instance.create_ppt_from_text(
        query_text=direct_ppt_query,
        template_id=selected_template_id,
        author="原生调用精简测试",
        is_card_note=False, 
        ai_image_type="normal"
    )

    # --- 轮询任务结果 ---
    if sid_direct:

        ppt_url_direct = ppt_gen_instance.poll_for_result(sid_direct, poll_interval=15, max_attempts=20)
        if ppt_url_direct:
            print(f"\n🎉 最终PPT已生成！下载链接: {ppt_url_direct}")
        else:
            # poll_for_result 方法内部会打印失败或超时的信息
            print("\nPPT生成失败或超时，请查看上方日志了解详情。")
    else:
        # create_ppt_from_text 方法内部会打印失败的信息
        print("\n启动PPT创建任务失败，请查看上方日志了解详情。")

    print("\n--- 智能PPT生成器演示流程结束 ---")